

#include "acceptor_win.h"
#include "event_loop.h"
#include "utils.h"

#include <array>

using namespace as::snet;

bool IOContext::post(socket_fd listen_fd, int af)
{
	auto sptr = BaseSocket::create_socket_obj(af);
	if(sptr == nullptr)
	{
		return false;
	}

	this->clear();
	sptr->open(af);
	socket = sptr->get_fd();

	std::cout << " create post socket:" << socket << std::endl;

	DWORD dw = 0;
	constexpr auto size = sizeof(SOCKADDR_STORAGE) + 16;
	if (BaseSocket::acceptex(listen_fd, socket, wsaBuf.buf, 0, size, size, &dw, &overlapped) == false)
	{
		auto err = BaseSocket::get_last_err();
		if(ERROR_IO_PENDING != err)
		{
			std::cout << "accept error:" << err << std::endl;
			delete sptr;
			BaseSocket::close(socket);
			socket = ERROR_SOCKET_FD;
			return false;
		} 
	}

	return true;
}

class Acceptor::impl::WaitingIo
{
public:
	WaitingIo() {}
	~WaitingIo()
	{
		for(auto &io : m_ios)
		{
			io.reset();
		}
	}

	void post_one(socket_fd lfd, int af)
	{
		for(auto &io : m_ios)
		{
			if(io.socket == ERROR_SOCKET_FD)
			{
				io.post(lfd, af);
				break;
			}
		}
	}

	void post_all(socket_fd lfd, int af)
	{
		for(auto &io : m_ios)
		{
			io.post(lfd, af);
		}
	}

	std::array<IOContext, IO_CONTEXT_COUNT> m_ios;
};

Acceptor::impl::impl(Acceptor *acc, std::shared_ptr<EventLoop> &ev)
	:m_ev(ev), 
	m_waitingIo(new WaitingIo)
{
	m_poller = std::make_shared<Poller>(*this);
	event_base eb;
	eb.m_handle_ptr = acc;
	eb.m_event_type = event_base::EV_ACCEPT_UPDATE;
	m_ev->add_event(eb);
}

Acceptor::impl::~impl()
{
	delete m_waitingIo;
}

bool Acceptor::impl::start(const EndPoint &ep)
{
	auto newObj = BaseSocket::create_socket_obj(ep.get_af());
	if(newObj == nullptr)
	{
		std::cout << "last err:" << BaseSocket::get_last_err() << std::endl;;
		return false;
	}

	int ret = newObj->bind(ep);
	if(ret == -1)
	{
		return false;
	}

	ret = newObj->listen(10);
	if(ret == -1)
	{
		return false;
	}

	m_ep = ep;
	m_ac_socket.reset(newObj);
	m_poller->add(newObj->get_fd(), newObj);

	//post all event
	m_waitingIo->post_all(newObj->get_fd(), ep.get_af());

	m_running = true;

	return true;
}

void Acceptor::impl::stop()
{
	m_running = false;
}

void Acceptor::impl::update()
{
	if(m_running == false) 
		return;

	m_poller->poll();
	if(m_ac_socket)
	{	
		m_waitingIo->post_one(m_ac_socket->get_fd(), m_ep.get_af());
	}
}

void Acceptor::impl::on_poll(void *key, void *ev)
{
	const auto e = static_cast<OVERLAPPED_ENTRY*>(ev);
	IOContext *io = CONTAINING_RECORD(e->lpOverlapped, IOContext, overlapped);
	if(io == nullptr || io->socket == ERROR_SOCKET_FD)
	{
		return;
	}

	event_base eb;
	eb.m_event_type = io->ntype;
	eb.m_fd = io->socket;
 	if(!setsockopt(io->socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (const char*)&m_ac_socket, sizeof(m_ac_socket)))
	{
		BaseSocket::close(io->socket);
		io->reset();
		return;
	}

	m_ev->add_event(eb);

	//reset io
	io->reset();
}
